/*
 * Copyright (c) 2005, Swedish Institute of Computer Science
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the Contiki operating system.
 *
 */

 /**
  * \addtogroup process
  * @{
  */

  /**
   * \file
   *         Implementation of the Contiki process kernel.
   * \author
   *         Adam Dunkels <adam@sics.se>
   *
   */

#include <stdio.h>

#include "sevenbox/fakeos/fakeos.h"

   /*
    * A configurable function called after a process poll been requested.
    */
#ifdef PROCESS_CONF_POLL_REQUESTED
#define PROCESS_POLL_REQUESTED PROCESS_CONF_POLL_REQUESTED
void PROCESS_POLL_REQUESTED(void);
#else
#define PROCESS_POLL_REQUESTED()
#endif

    /*
     * Pointer to the currently running process structure.
     */
     // struct fakeos_process *process_list;
     // struct fakeos_process *process_current;

     // static fakeos_event_t lastevent;

     /*
      * Structure used for keeping the queue of active events.
      */


#if PROCESS_CONF_STATS
fakeos_process_num_events_t process_maxevents;
#endif

// static volatile unsigned char poll_requested;

#define PROCESS_STATE_NONE        0
#define PROCESS_STATE_RUNNING     1
#define PROCESS_STATE_CALLED      2

static void call_process(struct fakeos_process_context *c, struct fakeos_process* p, fakeos_event_t ev, fakeos_process_data_t data);
static void exit_process(struct fakeos_process_context *c, struct fakeos_process* p, const struct fakeos_process* fromprocess);
static void do_poll(struct fakeos_process_context *c);
static void do_event(struct fakeos_process_context *c);

#define DEBUG 1
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif

#ifdef _MSC_VER
#  pragma alloc_text(FAKEOS_TEXT_SECTION, call_process, process_start, exit_process, process_exit, process_init,do_poll, do_event, process_run, process_nevents, process_post, process_post_synch, process_poll, process_is_running)
#endif

/*---------------------------------------------------------------------------*/
// fakeos_event_t process_alloc_event(void)
// {
  // return lastevent++;
// }
/*---------------------------------------------------------------------------*/
void process_start(struct fakeos_process_context* c, struct fakeos_process* p, fakeos_process_data_t data)
{
    struct fakeos_process* q;

    /* First make sure that we don't try to start a process that is
       already running. */
    for (q = c->process_list; q != p && q != NULL; q = q->next);

    /* If we found the process on the process list, we bail out. */
    if (q == p){
        return;
    }
    /* Put on the procs list.*/
    p->next = c->process_list;
    p->context = c;
    c->process_list = p;
    p->state = PROCESS_STATE_RUNNING;
    PT_INIT(&p->pt);

    PRINTF("process: starting '%s'\n", PROCESS_NAME_STRING(p));

    /* Post a synchronous initialization event to the process. */
    process_post_synch(c, p, EV_PROCESS_SYS_INIT, data);
}
/*---------------------------------------------------------------------------*/
static void exit_process(struct fakeos_process_context *c, struct fakeos_process* p, const struct fakeos_process* fromprocess)
{
    register struct fakeos_process* q;
    struct fakeos_process* old_current = c->process_current;

    PRINTF("process: exit_process '%s'\n", PROCESS_NAME_STRING(p));

    /* Make sure the process is in the process list before we try to
       exit it. */
    for (q = c->process_list; q != p && q != NULL; q = q->next);
    if (q == NULL){
        return;
    }

    if (process_is_running(c, p)){
        /* Process was running */

        if (p->thread != NULL && p != fromprocess){
            /* Post the exit event to the process that is about to exit. */
            c->process_current = p;
            p->thread(&p->pt, EV_PROCESS_SYS_EXIT, NULL);
        }
    }

    if (p == c->process_list){
        c->process_list = c->process_list->next;
    }
    else{
        for (q = c->process_list; q != NULL; q = q->next){
            if (q->next == p){
                q->next = p->next;
                break;
            }
        }
    }

    if (process_is_running(c, p)){
        /* Process was running */
        p->state = PROCESS_STATE_NONE;

        /*
         * Post a synchronous event to all processes to inform them that
         * this process is about to exit. This will allow services to
         * deallocate state associated with this process.
         */
        for (q = c->process_list; q != NULL; q = q->next){
            call_process(c, q, EV_PROCESS_SYS_EXITED, (fakeos_process_data_t)p);
        }
    }

    c->process_current = old_current;
}
/*---------------------------------------------------------------------------*/
static void call_process(struct fakeos_process_context *c, struct fakeos_process* p, fakeos_event_t ev, fakeos_process_data_t data)
{
    int ret;

#if DEBUG
    if (p->state == PROCESS_STATE_CALLED){
        printf("process: process '%s' called again with event %d\n", PROCESS_NAME_STRING(p), ev);
    }
#endif /* DEBUG */

    if ((p->state & PROCESS_STATE_RUNNING) &&
        p->thread != NULL){
        PRINTF("process: calling process '%s' with event %d\n", PROCESS_NAME_STRING(p), ev);
        c->process_current = p;
        p->state = PROCESS_STATE_CALLED;
        ret = p->thread(&p->pt, ev, data);
        if (ret == PT_EXITED ||
            ret == PT_ENDED ||
            ev == EV_PROCESS_SYS_EXIT){
            exit_process(c, p, p);
        }
        else{
            p->state = PROCESS_STATE_RUNNING;
        }
    }
}
/*---------------------------------------------------------------------------*/
void
process_exit(struct fakeos_process_context *c,struct fakeos_process* p)
{
    exit_process(c, p, c->process_current);
}
/*---------------------------------------------------------------------------*/
void process_init(struct fakeos_process_context* c)
{
    //初始化fakeos_process_context
    c->process_list = NULL;
    c->process_current = NULL;
    c->poll_requested = 0;
    c->event_queue.buffer = (void*)&c->events;
    c->event_queue.element_size = sizeof(struct fakeos_event_data);
    c->event_queue.size = PROCESS_CONF_NUMEVENTS;
    c->event_queue.push_count = 0;
    c->event_queue.pop_count = 0;
}
/*---------------------------------------------------------------------------*/
/*
 * Call each process' poll handler.
 */
 /*---------------------------------------------------------------------------*/
static void do_poll(struct fakeos_process_context *c)
{
    struct fakeos_process* p;

    c->poll_requested = 0;
    /* Call the processes that needs to be polled. */
    for (p = c->process_list; p != NULL; p = p->next){
        if (p->needspoll){
            p->state = PROCESS_STATE_RUNNING;
            p->needspoll = 0;
            call_process(c, p, EV_PROCESS_SYS_POLL, NULL);
        }
    }
}
/*---------------------------------------------------------------------------*/
/*
 * Process the next event in the event queue and deliver it to
 * listening processes.
 */
 /*---------------------------------------------------------------------------*/
static void do_event(struct fakeos_process_context *c)
{
    fakeos_event_t ev;
    fakeos_process_data_t data;
    struct fakeos_process* receiver;
    struct fakeos_process* p;
    struct fakeos_event_data event;

    /*
     * If there are any events in the queue, take the first one and walk
     * through the list of processes to see if the event should be
     * delivered to any of them. If so, we call the event handler
     * function for the process. We only process one event at a time and
     * call the poll handlers inbetween.
     */

    if (circbuf_counts(&c->event_queue) > 0){

        /* There are events that we should deliver. */
        circbuf_pop(&c->event_queue, &event, 0);

        ev = event.ev;

        data = event.data;
        receiver = event.p;

        /* If this is a broadcast event, we deliver it to all events, in
           order of their priority. */
        if (receiver == PROCESS_BROADCAST){
            for (p = c->process_list; p != NULL; p = p->next){

                /* If we have been requested to poll a process, we do this in
                   between processing the broadcast event. */
                if (c->poll_requested){
                    do_poll(c);
                }
                call_process(c, p, ev, data);
            }
        }
        else{
            /* This is not a broadcast event, so we deliver it to the
               specified process. */
               /* If the event was an INIT event, we should also update the
                  state of the process. */
            if (ev == EV_PROCESS_SYS_INIT){
                receiver->state = PROCESS_STATE_RUNNING;
            }

            /* Make sure that the process actually is running. */
            call_process(c, receiver, ev, data);
        }
    }
}
/*---------------------------------------------------------------------------*/
int process_run(struct fakeos_process_context *c)
{
    /* Process poll events. */
    if (c->poll_requested){
        do_poll(c);
    }

    /* Process one event from the queue */
    do_event(c);

    return process_nevents(c);
}
/*---------------------------------------------------------------------------*/
int process_nevents(struct fakeos_process_context *c)
{
    return circbuf_counts(&c->event_queue) + c->poll_requested;
}
/*---------------------------------------------------------------------------*/
int process_post(strcut fakeos_process_context *c, struct fakeos_process* p, fakeos_event_t ev, fakeos_process_data_t data)
{
    int nevents = circbuf_counts(&c->event_queue);
    struct fakeos_event_data event;

    if (c == NULL)
    {
        PRINTF("process_post: process(%s) NULL context\n", PROCESS_NAME_STRING(p)); 
        return PROCESS_ERR_NOCONTEXT;
    }

    if (c->process_current == NULL){
        PRINTF("process_post: NULL process posts event %d to process '%s', nevents %d\n",
            ev, PROCESS_NAME_STRING(p), nevents);
    }
    else{
        PRINTF("process_post: Process '%s' posts event %d to process '%s', nevents %d\n",
            PROCESS_NAME_STRING(c->process_current), ev,
            p == PROCESS_BROADCAST ? "<broadcast>" : PROCESS_NAME_STRING(p), nevents); 
    }

    if (nevents == PROCESS_CONF_NUMEVENTS){
#if DEBUG
        if (p == PROCESS_BROADCAST){
            printf("soft panic: event queue is full when broadcast event %d was posted from %s\n", ev, PROCESS_NAME_STRING(c->process_current));
        }
        else{
            printf("soft panic: event queue is full when event %d was posted to %s from %s\n", ev, PROCESS_NAME_STRING(p), PROCESS_NAME_STRING(c->process_current));
        }
#endif /* DEBUG */
        return PROCESS_ERR_FULL;
    }

    // snum = (fakeos_process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
    event.ev = ev;
    event.data = data;
    event.p = p;
    nevents++;

    circbuf_push(&c->event_queue, &event);

#if PROCESS_CONF_STATS
    if (nevents > process_maxevents){
        process_maxevents = nevents;
    }
#endif /* PROCESS_CONF_STATS */

    return PROCESS_ERR_OK;
}
/*---------------------------------------------------------------------------*/
void process_post_synch(struct fakeos_process_context *c, struct fakeos_process* p, fakeos_event_t ev, fakeos_process_data_t data)
{
    struct fakeos_process* caller = c->process_current;

    call_process(c, p, ev, data);
    c->process_current = caller;
}
/*---------------------------------------------------------------------------*/
void process_poll(struct fakeos_process_context *c, struct fakeos_process* p)
{
    if (p != NULL){
        if (p->state == PROCESS_STATE_RUNNING || p->state == PROCESS_STATE_CALLED){
            p->needspoll = 1;
            c->poll_requested = 1;
            PROCESS_POLL_REQUESTED();
        }
    }
}
/*---------------------------------------------------------------------------*/
int process_is_running(struct fakeos_process_context *c, struct fakeos_process* p)
{
    return p->state != PROCESS_STATE_NONE;
}


/*---------------------------------------------------------------------------*/
/** @} */
